﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace ELearning.Wpf.UI
{
    using System.Diagnostics;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Security.Cryptography;

    using ELearning.Data.LearningDatasetTableAdapters;

    /// <summary>
    /// Interaction logic for wndLogin.xaml
    /// </summary>
    public partial class wndLogin : Window
    {
        private List<string> _lastLoginedUsers;

        private const int MaxUserNames = 7;
        private const string LastUsersFile = "lastUsers.dat";

        public wndLogin()
        {
            InitializeComponent();

            _lastLoginedUsers = TryDeserializeLastUsers();

            cb_Users.Items.Clear();
            if (_lastLoginedUsers == null)
            {
                _lastLoginedUsers = new List<string>();
                return;
            }
            
            foreach (var user in _lastLoginedUsers)
            {
                cb_Users.Items.Add(user);
            }

            if (_lastLoginedUsers.Count > 0)
            {
                cb_Users.Text = _lastLoginedUsers[0];
                tb_Password.Focus();
            }
        }

        private List<string> TryDeserializeLastUsers()
        {
            string lastUsersFile = GetLastUsersFilePath;
            List<string> returnValue = null;
            if (File.Exists(lastUsersFile))
            {
                using (var stream = File.OpenRead(lastUsersFile))
                {
                    
                    BinaryFormatter bf = new BinaryFormatter();
                    try
                    {
                        returnValue = bf.Deserialize(stream) as List<string>;
                    }
                    catch (Exception e)
                    {
                        DebugHelper.ShowDebugMessageBox("Failed to deserialize last users list.\n" + e);
                    }
                }
            }

            return returnValue;
        }

        private static string GetLastUsersFilePath
        {
            get
            {
                return Path.Combine(Directory.GetCurrentDirectory(), LastUsersFile);
            }
        }

        private void bt_login_Click(object sender, RoutedEventArgs e)
        {
            PerformLogin();
        }

        private void PerformLogin()
        {
            lb_Error.Visibility = Visibility.Hidden;

            var _usersTableAdapter = new UsersTableAdapter();
            const string dateFormat = "yyyy-MM-dd hh:mm";

            //check user count
            var overral = (long)_usersTableAdapter.GetUsersCount();
            if (overral <= 0)
            {
                MessageBox.Show(
                    "У системi не знайдено жодного користувача. Буде автоматично додано користувача admin.",
                    DebugHelper.ProductName,
                    MessageBoxButton.OK,
                    MessageBoxImage.Information);
                _usersTableAdapter.Insert(
                    "admin", null, DateTime.Now.ToString(dateFormat), null, true, "Системный администратор");
                cb_Users.Text = "admin";
                goto FirstLoginAsAdmin;
            }

            var idCount = _usersTableAdapter.SelectUserCount(cb_Users.Text);

            if ((long)idCount <= 0)
            {
                lb_Error.Content = "Користувача не знайдено.";
                lb_Error.Visibility = Visibility.Visible;
                return;
            }

            FirstLoginAsAdmin:
            byte[] hash = (byte[])_usersTableAdapter.GetPasswordHash(cb_Users.Text);
            if (hash == null)
            {
                if (ShowNewPasswordForm((long)_usersTableAdapter.GetUserID(cb_Users.Text)) != true)
                {
                    return;
                }

                hash = (byte[])_usersTableAdapter.GetPasswordHash(cb_Users.Text);
            }

            byte[] inputHash = MD5Helper.GetHashFromString(tb_Password.Password);

            var userId = _usersTableAdapter.GetUserID(cb_Users.Text) as long?;
            if (userId == null || !hash.SequenceEqual(inputHash))
            {
                lb_Error.Content = "Пароль ведено невiрно!";
                lb_Error.Visibility = Visibility.Visible;
                return;
            }

            _usersTableAdapter.UpdateLastAccessDate(DateTime.Now.ToString(dateFormat), userId.Value);

            SessionManager.CurrentUserID = userId.Value;
            SessionManager.IsAdmin = _usersTableAdapter.IsAdmin(userId.Value).GetValueOrDefault(false);

            this.AddLoginNameToRecents();

            wndSubjectSelect subjectSelect = new wndSubjectSelect();
            this.Close();
            subjectSelect.ShowDialog();
        }

        private bool? ShowNewPasswordForm(long getUserId)
        {
            wndNewPassword wndNewPassword = new wndNewPassword(getUserId, false);
            return wndNewPassword.ShowDialog();
        }

        private void AddLoginNameToRecents()
        {
            if (this._lastLoginedUsers.Contains(this.cb_Users.Text))
            {
                this._lastLoginedUsers.Remove(this.cb_Users.Text);
            }

            this._lastLoginedUsers.Insert(0, this.cb_Users.Text);
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            this.TrySerializeLastUserList(_lastLoginedUsers);
        }

        private void TrySerializeLastUserList(List<string> userList)
        {
            string lastUsersFile = GetLastUsersFilePath;
            using (FileStream fs = new FileStream(lastUsersFile, FileMode.Create))
            {
                BinaryFormatter bf = new BinaryFormatter();
                try
                {
                    this.EnsureCapacity();
                    bf.Serialize(fs, userList);
                }
                catch (Exception exception)
                {
                    DebugHelper.ShowDebugMessageBox("Failed to serialize last users list.\n" + exception);
                }
            }
        }

        private void EnsureCapacity()
        {
            if (_lastLoginedUsers.Count <= MaxUserNames)
            {
                return;
            }

            // removes first entries
            _lastLoginedUsers.RemoveRange(MaxUserNames, _lastLoginedUsers.Count - MaxUserNames);
        }

        private void Window_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                PerformLogin();
            }
        }
    }

    internal static class SessionManager
    {
        public static long CurrentUserID;

        public static bool IsAdmin;
    }
}
